/******************************************************************************
copyright(c)cguage.com
source code can be free to use and publish, but please keep header comments, thx

author:  cguage
contact: tangcraig@gmail.com
desc:    missing
date:    unknown

*******************************************************************************/
#pragma once

class Job {
protected:
    enum Priority {URGENT, LOW};

public:
    Job() : _hasRunSignal(false), _curTask("_init") {
        _jobs.push_back(this);
    }

    static void doJobs() {
        Job* job = NULL;
        for (vector<Job*>::iterator iter = _jobs.begin(); iter != _jobs.end(); ++iter) {
            job = *iter;
            // build reflection table
            _reflectionTbl[job->name()] = job;

            // start thread func
            _jobThreadTbl[job->name()] = thread(&taskProc, job);

            // call init
            job->onInit();
        }

        // start job-switching timer thread 
        thread timerThread(&Job::timerProc);

        // wait all threads to end
        for (map<string, thread>::iterator iter = _jobThreadTbl.begin(); 
        iter != _jobThreadTbl.end(); ++iter) {
            iter->second.join();
        }
        timerThread.join();
    }

protected:
    virtual string name() = 0;
    virtual void run(const string& strParams) = 0;
    virtual void onInit() {}

    void notifyAnother(string jobName, string task, Priority priority) {
        // notify self is forbiden, cause _taskLock will be used recursivly
        if (jobName == name()) {
            cout << "warning: notifying self!" << jobName << endl;
            return;
        }

        ReflectionIter iter = _reflectionTbl.find(jobName);
        assert(iter != _reflectionTbl.end());
        Job* job = iter->second;

        // add Q or signal right now
        job->_taskLock.lock();
        if (LOW == priority) {
            job->_taskQ.push(task);
        } else if (URGENT == priority) {
            job->_curTask = task;
            job->signalRun();
        }

        job->_taskLock.unlock();
    }

    inline std::string getCurrentTime() {
        static char fmt[32];
        time_t t = time(0);
        tm inf;
        localtime_s(&inf, &t);
        strftime(fmt, sizeof(fmt) / sizeof(fmt[0]), "%Y-%m-%d %X", &inf);
        return fmt;
    }

    inline void log(const std::ostringstream& oss, Priority priority = LOW) {
        notifyAnother("WriteLog", oss.str(), priority);
    }

private:
    inline void signalRun() {
        unique_lock<mutex> lock(_signalLock);
        _hasRunSignal = true;
        _runSignal.notify_all();
    }

    inline void waitRunSignal() {
        // use external variable to guarantee signal be retrieved 
        while (!_hasRunSignal) {
            unique_lock<mutex> lock(_signalLock);
            _runSignal.wait(lock);
        }
        _hasRunSignal = false;
    }

    void taskProc() {
        while (true) {
            waitRunSignal();
            _taskLock.lock();

            // cout << name() << ".taskProc" << endl;
            // call subclass function to do job task
            if (!_curTask.empty()) {
                // urgent task 
                run(_curTask);
                _curTask = "";
            } else if (!_taskQ.empty()) {
                do {
                    run(_taskQ.front());
                    _taskQ.pop();
                } while (!_taskQ.empty());
            } else {
                run("");
            }

            _taskLock.unlock();
        }
    }

    static void timerProc() {
        const size_t jobCnt = _jobs.size();

        while (true) {
            for (vector<Job*>::iterator iter = _jobs.begin();
            iter != _jobs.end(); ++iter) {
                // cout << "Timer: signal " << (*iter)->name() << endl;
                (*iter)->signalRun();
                //this_thread::sleep_for(chrono::seconds(1));
                this_thread::sleep_for(chrono::milliseconds(500));
            }
        }
    }


private:
    typedef map<string, Job*>::iterator ReflectionIter;
    static vector<Job*>        _jobs;
    static map<string, thread> _jobThreadTbl;
    static map<string, Job*>   _reflectionTbl;

    queue<string>              _taskQ;
    string                     _curTask;
    mutex                      _taskLock;
    mutex                      _signalLock;
    condition_variable         _runSignal;
    bool                       _hasRunSignal;
};
vector<Job*>        Job::_jobs;
map<string, thread> Job::_jobThreadTbl;
map<string, Job*>   Job::_reflectionTbl;